package com.gitee.cnlongdb.server.operator.remove;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.gitee.cnlongdb.basic.constant.ConcurrentDataMap;
import com.gitee.cnlongdb.basic.constant.LongDbConstant;
import com.gitee.cnlongdb.basic.entity.database.LongTableEntity;
import com.gitee.cnlongdb.basic.entity.query.LongDbQuery;
import com.gitee.cnlongdb.basic.entity.update.UpdateDateEntity;
import com.gitee.cnlongdb.basic.utils.FieldHandlerUtils;
import com.gitee.cnlongdb.basic.utils.GenerateSequenceUtils;
import com.gitee.cnlongdb.server.connect.LongDbConnect;
import com.gitee.cnlongdb.server.datasync.LongdbMasterSalveDataSync;

/**
 * 系统名称：RemoveOperator.java
 * 模块名称：
 * 模块描述：longdb - 删除操作服务实现层
 * 功能列表：
 * 模块作者：ZOUYONG
 * 开发时间：2018年12月5日 下午10:01:20
 * 模块路径：com.gitee.cnlongdb.server.operator.remove.RemoveOperator
 * 更新记录：
 */
public class RemoveOperator {

	private final Logger logger = (Logger) LoggerFactory.getLogger(RemoveOperator.class);
	
	// 数据库连接对象
	private LongDbConnect connect;
	public RemoveOperator(LongDbConnect connect) {
		logger.debug(LongDbConstant.longdbDefaultName + "初始化[删除执行器]...");
		this.connect = connect;
	}

	/**
	 * 功能描述：删除指定表中数据
	 * 模块作者：ZOUYONG
	 * 开发时间：2018年11月11日 上午9:22:16
	 * 更新记录：
	 * 返回数据：boolean
	 */
	public boolean remove(String tableName) {
		if(StringUtils.isBlank(tableName)) {
			return false;
		}
		LongTableEntity table = connect.getCreateOperator().getLongTableByName(tableName);
		if(null == table) {
			return false;
		}
		table.setDataBaseRootDirectory(connect.getDataBaseRootDirectory());
		File tableFile = new File(connect.getDataBaseRootDirectory() + table.getFilePath() + File.separator + LongDbConstant.longdbTableDataHouse);
		if(!tableFile.exists()) {
			tableFile.mkdirs();
			return false;
		}
		System.gc();
		File newFile = new File(tableFile.getParentFile().getAbsolutePath() + File.separator + LongDbConstant.longdbTableDataHouse + LongDbConstant.longdbDeleteFlag);
		if(tableFile.renameTo(newFile)) {
			// 清空表的总数量相关数据
			table.getDataFileRowCountMap().clear();
			// 更新表的配置文件内容
			connect.getCreateOperator().updateTableConfigFileContent(table, false);
			// 处理主从数据同步
			LongdbMasterSalveDataSync.masterSlaveSyncData(connect, LongDbConstant.longdbActuatorRemove, "remove", tableName);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 功能描述：批量删除文件及文件中子文件
	 * 模块作者：ZOUYONG
	 * 开发时间：2018年11月11日 上午10:08:29
	 * 更新记录：
	 * 返回数据：void
	 */
	public void deleteFile(File file) {
		if(file.isDirectory()) {
			File[] files = file.listFiles();
			for (File f : files) {
				deleteFile(f);
			}
		}
		System.gc(); // 强制删除，解除该文件被别人占用
		file.delete();
	}

	/**
	 * 功能描述：删除查询出来后的数据
	 * 模块作者：ZOUYONG
	 * 开发时间：2018年11月11日 上午11:21:58
	 * 更新记录：
	 * 返回数据：boolean
	 */
	public boolean remove(LongDbQuery query) throws Exception {
		if(null == query || null == query.getClassz()) {
			return false;
		}
		
		Class<?> cls = query.getClassz();
		String tableName = StringUtils.isBlank(query.getTableName()) ? cls.getSimpleName() : query.getTableName();
		LongTableEntity table = connect.getCreateOperator().getLongTableByName(tableName);
		if(null == table) {
			return false;
		}
		
		List<String> list = connect.getQueryOperator().getTableContentJson(query, cls, tableName);
		if(CollectionUtils.isEmpty(list)) {
			return false;
		}

		removeDataByTransacation(list, tableName);
		return true;
	}

	/**
	 * 功能描述：根据事务情况删除数据
	 * 模块作者：ZOUYONG
	 * 开发时间：2018年12月8日 下午12:20:48
	 * 更新记录：
	 * 返回数据：void
	 */
	private void removeDataByTransacation(List<String> list, String tableName) throws Exception {
		// 获取当前的事务名称
		String transactionName = Thread.currentThread().getName();
		boolean isOpenTrans = StringUtils.isNotBlank(transactionName) && transactionName.endsWith(LongDbConstant.longdbTranscationFlag);
		try {
			for (String jsonStr : list) {
				UpdateDateEntity updDelData = new UpdateDateEntity();
				updDelData.setDataContent(jsonStr);
				updDelData.setTableName(tableName);
				updDelData.setRemoveOrUpdate(true);
				if(isOpenTrans) { // 开启事务
					List<UpdateDateEntity> updDelDataList = ConcurrentDataMap.tempUpdDelDataMap.get(transactionName);
					if(null == updDelDataList) {
						updDelDataList = new ArrayList<UpdateDateEntity>();
					}
					updDelDataList.add(updDelData);
					ConcurrentDataMap.tempUpdDelDataMap.put(transactionName, updDelDataList);
				}
				// 未开启事务
				else {
					updateTableFileLineData(updDelData);
				}
			}
		}
		catch(Exception e) {
			throw new RuntimeException("删除数据操作异常："+e.getMessage(), e);
		}
	}

	/**
	 * 功能描述：修改表中某行的数据
	 × 模块作者：ZOUYONG
	 × 开发时间：2018年11月28日 下午5:32:09
	 × 返回数据：void
	 */
	public void updateTableFileLineData(UpdateDateEntity updDelData) throws Exception {
		
		String jsonStr = updDelData.getDataContent();
		String tableName = updDelData.getTableName();
		boolean removeOrUpdate = updDelData.isRemoveOrUpdate();
		
		RandomAccessFile fout = null;
		FileChannel fcout = null;
		FileLock flout = null;
		try {
			LongTableEntity tab = FieldHandlerUtils.parserLongStrToObject(jsonStr, LongTableEntity.class);
			LongTableEntity table = connect.getCreateOperator().getLongTableByName(tableName);
			if(null == table) {
				throw new RuntimeException("未获取到表的基本数据信息！");
			}
			
			String fileName = tab.getLongId().substring(10, tab.getLongId().length());
			File tabFile = connect.getQueryOperator().getFileByFileName(tableName, fileName);
			if(null == tabFile || !tabFile.exists()) {
				throw new RuntimeException("未找到要删除的数据信息！");
			}
			
			fout = new RandomAccessFile(tabFile, "rw");
			long filelength = fout.length(); // 获取文件的长度
			fcout = fout.getChannel(); // 打开文件通道
			while(true) {
				try {
					flout = fcout.tryLock(); // 循环文件上锁
					break;
				} catch(Exception e) {
					logger.error("文件上锁异常！");
				}
			}

			fout.seek(0); // 将指针移到文件的开头
			// 一次性读取，就是把文件中的内容都读取到字节数组中
			byte [] buffer=new byte[(int)filelength];
			fout.read(buffer);
			String content = new String(buffer, LongDbConstant.longdbSystemCharset);

			String[] array = content.split(LongDbConstant.longdbTableChangeLine);
			Integer position = GenerateSequenceUtils.getLongDataOrder(tab.getLongId());
			if(position < 0 || array.length < position) {
				throw new RuntimeException("未找到要" + (removeOrUpdate ? "删除" : "修改") + "的数据！");
			}
			
			StringBuffer text = new StringBuffer();
			if(StringUtils.isNotBlank(array[position-1])) {
				// 删除数据
				if(removeOrUpdate) {
					array[position-1] = "";
				}
				// 修改数据
				else {
					array[position-1] = jsonStr;
				}
			}
			boolean isValue = true;
			// 重新整理表中数据
			for (String str : array) {
				if(isValue && StringUtils.isNotBlank(str)) {
					isValue = false;
				}
				text.append(str).append(LongDbConstant.longdbTableChangeLine);
			}
			if(isValue) {
				text.setLength(0);
			}
			fout.setLength(0); // 清空文件中数据
			// 重新将数据写入表中
			fout.write(text.toString().getBytes());
			flout.release();
			
			// 更新表中总数量
			updateTableTotalCount(table, fileName);
		}
		catch(Exception e) {
			throw new RuntimeException("修改数据操作异常: " + e.getMessage(), e);
		}
		finally {
			if(fcout != null) {
			    try {
			        fcout.close();
			    } catch (IOException e) {
			        fcout = null;
			    }
			}
			if(fout != null) {
			    try {
			        fout.close();
			    } catch (IOException e) {
			        fout = null;
			    }
			}
			if(null != flout) {
				try {
					flout.release();
					flout.close();
			    } catch (IOException e) {
			    	flout = null;
			    }
			}
        }
		// 处理主从数据同步
		LongdbMasterSalveDataSync.masterSlaveSyncData(connect, LongDbConstant.longdbActuatorRemove, "updateTableFileLineData", updDelData);
	}
	
	/**
	 * 功能描述：更新表的总数量
	 × 模块作者：ZOUYONG
	 × 开发时间：2018年11月28日 下午6:04:58
	 × 返回数据：void
	 */
	public void updateTableTotalCount(LongTableEntity table, String fileName) {
		// 更新表对应的文件行数
		Integer oldDeleteNumber = table.getDataFileRowCountMap().get(fileName+LongDbConstant.longdbDeleteFlag);
		if(null == oldDeleteNumber) {
			oldDeleteNumber = -1;
		} else {
			oldDeleteNumber -= 1;
		}
		table.getDataFileRowCountMap().put(fileName+LongDbConstant.longdbDeleteFlag, oldDeleteNumber);
		connect.getCreateOperator().updateTableConfigFileContent(table, false);
	}

	/**
	 * 功能描述：删除表和数据
	 * 模块作者：ZOUYONG
	 * 开发时间：2018年11月11日 上午10:59:21
	 * 更新记录：
	 * 返回数据：boolean
	 */
	public boolean drop(String tableName) {
		if(StringUtils.isBlank(tableName)) {
			return false;
		}
		LongTableEntity table = connect.getCreateOperator().getLongTableByName(tableName);
		if(null == table) {
			return false;
		}
		File tableFile = new File(connect.getDataBaseRootDirectory() + table.getFilePath());
		if(!tableFile.exists()) {
			return false;
		}
		System.gc();
		File newFile = new File(tableFile.getParentFile().getAbsolutePath() + File.separator + tableName + LongDbConstant.longdbDeleteFlag);
		boolean result = tableFile.renameTo(newFile);

		// 处理主从数据同步
		LongdbMasterSalveDataSync.masterSlaveSyncData(connect, LongDbConstant.longdbActuatorRemove, "drop", tableName);
		return result;
	}

	/**
	 * 功能描述：删除数据库
	 * 模块作者：ZOUYONG
	 * 开发时间：2018年11月28日 下午10:08:57
	 * 更新记录：
	 * 返回数据：boolean
	 */
	public boolean dropDataBase(String dataBaseName) {
		if(StringUtils.isBlank(dataBaseName)) {
			return false;
		}
		String dbCfgFilePath = connect.getDataBaseRootDirectory() + File.separator + dataBaseName;
		File dbCfgFile = new File(dbCfgFilePath);
		if(!dbCfgFile.exists()) {
			return false;
		}
		System.gc();
		File newFile = new File(dbCfgFile.getAbsolutePath() + LongDbConstant.longdbDeleteFlag);
		if(dbCfgFile.renameTo(newFile)) {
			// 处理主从数据同步
			LongdbMasterSalveDataSync.masterSlaveSyncData(connect, LongDbConstant.longdbActuatorRemove, "dropDataBase", dataBaseName);
			return true;
		} else {
			return false;
		}
	}

	/**
	 * 功能描述：根据ID删除数据
	 * 模块作者：ZOUYONG
	 * 开发时间：2018年12月8日 下午12:09:25
	 * 更新记录：
	 * 返回数据：void
	 */
	public void removeById(String longId, Class<?> cls) throws Exception {
		if(StringUtils.isBlank(longId) || null == cls) {
			return;
		}
		String tableName = cls.getSimpleName();
		String content = connect.getQueryOperator().findContentById(longId, tableName);
		if(StringUtils.isNotBlank(content)) {
			List<String> list = Arrays.asList(content);
			removeDataByTransacation(list, tableName);
		}
	}

}
